#include "stdafx.h"
#include "shellsimpleapp.h"
#include "owner_draw_menu.h"

HINSTANCE g_hinst = NULL;

// Set up common controls v6 the easy way.

HRESULT MakeOwnerDraw(HMENU hmenu, BOOL fOwnerDraw);
void FreeOwnerDrawData(HMENU hmenu);
void ResetMenuMetrics(HMENU hmenu);



CShellSimpleApp::CShellSimpleApp() : 
	_cRef(1), 
	_hdlg(NULL), 
	_hrOleInit(E_FAIL),
	_hmenu(NULL),
	_hmenuContext(NULL),
	_pMenu(NULL)
{

}

HRESULT CShellSimpleApp::DoModal( HWND hwnd )
{
	INT_PTR iRet = DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, s_DlgProc, (LPARAM)this);
	return (iRet == IDOK) ? S_OK : S_FALSE;
}

STDMETHODIMP CShellSimpleApp::QueryInterface( __in REFIID riid, __deref_out void **ppv )
{
	static const QITAB qit[] = 
	{
		{ 0 },
	};
	return QISearch(this, qit, riid, ppv);
}

STDMETHODIMP_(ULONG) CShellSimpleApp::AddRef()
{
	return InterlockedIncrement(&_cRef);
}

STDMETHODIMP_(ULONG) CShellSimpleApp::Release()
{
	LONG cRef = InterlockedDecrement(&_cRef);
	if (!cRef)
		delete this;
	return cRef;
}

BOOL CALLBACK CShellSimpleApp::s_DlgProc( HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
#pragma warning(push)
#pragma warning(disable:4312) // GetWindowLongPtr is not w4 clean.
	CShellSimpleApp *pssa = reinterpret_cast<CShellSimpleApp *>(GetWindowLongPtr(hdlg, DWLP_USER));
#pragma warning(pop)

	if (uMsg == WM_INITDIALOG)
	{
		pssa = reinterpret_cast<CShellSimpleApp *>(lParam);
		pssa->_hdlg = hdlg;
#pragma warning(push)
#pragma warning(disable:4244) // SetWindowLongPtr is not w4 clean.
		SetWindowLongPtr(hdlg, DWLP_USER, reinterpret_cast<LONG_PTR>(pssa));
#pragma warning(pop)
	}

	return pssa ? pssa->_DlgProc(uMsg, wParam, lParam) : FALSE;
}

CShellSimpleApp::~CShellSimpleApp()
{

}

void _SetDialogIcon(HWND hdlg, SHSTOCKICONID siid)
{
	SHSTOCKICONINFO sii = {sizeof(sii)};
	if (SUCCEEDED(SHGetStockIconInfo(siid, SHGFI_ICON | SHGFI_SMALLICON, &sii)))
	{
		SendMessage(hdlg, WM_SETICON, ICON_SMALL, (LPARAM) sii.hIcon);
	}
	if (SUCCEEDED(SHGetStockIconInfo(siid, SHGFI_ICON | SHGFI_LARGEICON, &sii)))
	{
		SendMessage(hdlg, WM_SETICON, ICON_BIG, (LPARAM) sii.hIcon);
	}
}

void _ClearDialogIcon(HWND hdlg)
{
	DestroyIcon((HICON)SendMessage(hdlg, WM_GETICON, ICON_SMALL, 0));
	DestroyIcon((HICON)SendMessage(hdlg, WM_GETICON, ICON_BIG, 0));
}

void CShellSimpleApp::_InitMenuTypeComboBox()
{
	HWND hwnd = GetDlgItem(_hdlg, IDC_MENUTYPE);
	SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)L"Standard Menu");
	SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)L"Classic Owner-draw Menu");
	SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)L"Vista Owner-draw Menu");
}

void CShellSimpleApp::_OnInitDlg()
{
	_SetDialogIcon(_hdlg, SIID_APPLICATION);

	_hrOleInit = OleInitialize(0);  // Needed for drag and drop

	_hmenu = LoadMenu(g_hinst, MAKEINTRESOURCE(IDM_CONTEXTMENU));
	if (_hmenu)
	{
		_hmenuContext = GetSubMenu(_hmenu, 0);
		_InitMenuTypeComboBox();
		SendDlgItemMessage(_hdlg, IDC_MENUTYPE, CB_SETCURSEL, MENUTYPE_VISTAOWNERDRAW, 0);
		_SetMenuType(MENUTYPE_VISTAOWNERDRAW);
	}
}

void CShellSimpleApp::_OnDestroyDlg()
{
	_ClearDialogIcon(_hdlg);

	if (SUCCEEDED(_hrOleInit))
	{
		OleUninitialize();
	}

	if (_hmenu)
	{
		FreeOwnerDrawData(_hmenuContext);
		DestroyMenu(_hmenu);
	}

	if (_pMenu)
	{
		_pMenu->Release();
	}
}

void CShellSimpleApp::_OnRightClick(WPARAM wParam, int x, int y)
{
	POINT ptClient = { x, y };
	ClientToScreen(_hdlg, &ptClient);

	int idSelected = TrackPopupMenuEx(_hmenuContext, TPM_RETURNCMD, ptClient.x, ptClient.y, _hdlg, NULL);
	_OnSelectMenuItem(idSelected);
}

void CShellSimpleApp::_SetMenuType(int iMenuType)
{
	HRESULT hr = S_OK;

	if (_pMenu)
	{
		_pMenu->Release();
		_pMenu = NULL;
	}

	_iMenuType = iMenuType;
	if (MENUTYPE_STANDARD == _iMenuType)
	{
		hr = MakeOwnerDraw(_hmenuContext, false);
	}
	else
	{
		hr = MakeOwnerDraw(_hmenuContext, true);
		if (FAILED(hr))
		{
			_iMenuType = MENUTYPE_STANDARD;
		}

		if (MENUTYPE_VISTAOWNERDRAW == _iMenuType)
		{
			_pMenu = static_cast<IOwnerDrawMenu *>(new  
				CVistaOwnerDrawMenu);
		}
		else
		{
			_pMenu = static_cast<IOwnerDrawMenu *>(new   
				CClassicOwnerDrawMenu);
		}

		if (_pMenu)
		{
			hr = _pMenu->Initialize(_hdlg);
			if (FAILED(hr))
			{
				// If you are not running in a theme that supports menu, 
				// styling falls back onto the classic version or standard version.
				_SetMenuType((MENUTYPE_CLASSICOWNERDRAW == _iMenuType) ? MENUTYPE_STANDARD : MENUTYPE_CLASSICOWNERDRAW);
			}
		}
	}

	// Clear out the menu metrics.
	if (SUCCEEDED(hr))
	{
		ResetMenuMetrics(_hmenuContext);
	}
}

BOOL CShellSimpleApp::_DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	BOOL bRet = TRUE;   // Default for all handled cases in switch below

	switch (uMsg)
	{
	case WM_INITDIALOG:
		_OnInitDlg();
		break;

	case WM_COMMAND:
		switch (GET_WM_COMMAND_ID(wParam, lParam))
		{
		case IDOK:
		case IDCANCEL:
			return EndDialog(_hdlg, IDOK == GET_WM_COMMAND_ID(wParam,   
				lParam));

		case IDC_MENUTYPE:
			if (CBN_SELENDOK == GET_WM_COMMAND_CMD(wParam, lParam))
			{
				int iCurSel = (int)SendDlgItemMessage(_hdlg,  
					IDC_MENUTYPE, CB_GETCURSEL, 0, 0);
				_SetMenuType(iCurSel);
			}
			break;
		}
		break;

	case WM_RBUTTONUP:
		_OnRightClick(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
		break;

	case WM_MEASUREITEM:
		_OnMeasureItem(reinterpret_cast<MEASUREITEMSTRUCT *>(lParam));
		break;

	case WM_DRAWITEM:
		_OnDrawItem(reinterpret_cast<DRAWITEMSTRUCT *>(lParam));
		break;

	case WM_SETTINGCHANGE:
		if (FAILED(_pMenu->SettingChange()))
		{
			_pMenu->Release();
			_pMenu = static_cast<IOwnerDrawMenu *>(new CClassicOwnerDrawMenu);
		}
		break;

	case WM_DESTROY:
		_OnDestroyDlg();
		break;

	default:
		bRet = FALSE;
	}
	return bRet;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
	g_hinst = hInstance;

	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
	if (SUCCEEDED(hr))
	{
		CShellSimpleApp *pdlg = new CShellSimpleApp();
		if (pdlg)
		{
			pdlg->DoModal(NULL);
			pdlg->Release();
		}

		CoUninitialize();
	}

	return 0;
}

// Owner-draw code

HRESULT MakeOwnerDraw(HMENU hmenu, BOOL fOwnerDraw)
{
	HRESULT hr = S_OK;

	for (int i = GetMenuItemCount(hmenu); SUCCEEDED(hr) && i > 0; --i)
	{
		MENUITEMINFO mii = {sizeof(mii) };
		mii.fMask = MIIM_FTYPE | MIIM_DATA;
		if (GetMenuItemInfo(hmenu, i-1, TRUE, &mii))
		{
			if (fOwnerDraw)
			{
				MENUITEMDATA *pmid = (MENUITEMDATA *)LocalAlloc(LPTR, sizeof(*pmid));
				if (pmid)
				{
					pmid->hmenu = hmenu;
					pmid->id = i-1;

					mii.dwItemData = reinterpret_cast<ULONG_PTR>(pmid);
					mii.fType |= MFT_OWNERDRAW;
				}
				else
				{ 
					mii.fType &= ~MFT_OWNERDRAW;
					hr = E_OUTOFMEMORY;
				}
			}
			else
			{
				LocalFree(reinterpret_cast<HLOCAL>(mii.dwItemData));
				mii.fType &= ~MFT_OWNERDRAW;
			}

			SetMenuItemInfo(hmenu, i-1, TRUE, &mii);
		}
	}

	return hr;
}

void FreeOwnerDrawData(HMENU hmenu)
{
	for (int i = GetMenuItemCount(hmenu); i > 0; --i)
	{
		MENUITEMINFO mii = {sizeof(mii) };
		mii.fMask = MIIM_FTYPE | MIIM_DATA;
		if (GetMenuItemInfo(hmenu, i-1, TRUE, &mii))
		{
			LocalFree(reinterpret_cast<HLOCAL>(mii.dwItemData));
		}
	}
}

BOOL GetMenuItem(ULONG_PTR itemData, __out_bcount(sizeof(MENUITEM)) MENUITEM *pItem)
{
	BOOL fRet = FALSE;

	if (itemData)
	{
		pItem->pmid = reinterpret_cast<MENUITEMDATA *>(itemData);
		pItem->szItemText[0] = 0;
		pItem->mii.cbSize = sizeof(pItem->mii);
		pItem->mii.fMask = MIIM_CHECKMARKS | MIIM_BITMAP | MIIM_STRING | MIIM_SUBMENU | MIIM_STATE | MIIM_FTYPE | MIIM_ID;
		pItem->mii.dwTypeData = pItem->szItemText;
		pItem->mii.cch = ARRAYSIZE(pItem->szItemText);
		fRet = GetMenuItemInfo(pItem->pmid->hmenu, pItem->pmid->id, TRUE, &pItem->mii);
	}

	return fRet;
}

BOOL CShellSimpleApp::_OnMeasureItem(__inout MEASUREITEMSTRUCT *pmis)
{
	MENUITEM menuItem;
	if (GetMenuItem(pmis->itemData, &menuItem))
	{
		_pMenu->MeasureItem(&menuItem, pmis);
	}
	else
	{
		pmis->itemWidth = pmis->itemHeight = 0;
	}
	return TRUE;
}

BOOL CShellSimpleApp::_OnDrawItem(__in DRAWITEMSTRUCT *pdis)
{
	if (!IsRectEmpty(&pdis->rcItem))
	{
		int iSaveDC = SaveDC(pdis->hDC);

		MENUITEM menuItem;
		if (GetMenuItem(pdis->itemData, &menuItem))
		{
			_pMenu->DrawItem(&menuItem, pdis);
		}

		RestoreDC(pdis->hDC, iSaveDC);
	}

	return FALSE;
}

void CShellSimpleApp::_OnSelectMenuItem(int idItem)
{
	if (idItem > 0)
	{
		if (_pMenu)
		{
			_pMenu->SelectedItem(idItem);
		}
	}
}
